/*
 * MainApplication.java
 *
 * Created on 14 mai 2007, 18:00
 *
 */
// TODO purge de la memoire du port serie par prediction du nb de secondes avant prochain evt
// TODO Lorsque l'on redémarre en mode record, ne pas remettre à 0 la trace
// TODO Faire marcher l'option single track de la conversion
// TODO changer les menus de calibrate pour passer à droite.
package courselog;

import courselog.gpsutils.Converters;
import courselog.gpsutils.PositionTracer;
import courselog.gpsutils.SerialGPSConnection;
import courselog.maputils.TiledMapBrowser;
import ewe.fx.Color;
import ewe.fx.Image;
import ewe.io.File;
import ewe.io.FileNotFoundException;
import ewe.io.FileReader;
import ewe.io.FileWriter;
import ewe.io.IOException;
import ewe.io.SerialPortOptions;
import ewe.sys.Convert;
import ewe.sys.Vm;

/**
 *
 * @author rigal
 */
public class MainApplication {
    // TODO load previous track on startup
    // TODO recover the last waypoint file and the selected / visit point
    
    /** Application icon to be used by all forms. */
    public Image myAppGlobalIcon = new Image("res/courseLog.png");
    /**
     * Version message displayed in the options panel="Version 1.0.0".
     */
    public static final String VERSIONMESSAGE="CourseLog Version 2.0.3"+
            "\n(17 July 2008)"+
            "\ncourselog.free.fr";
    
    /**
     * Options button color when GPS is OK RGB=(192,255,192).
     */
    public static final Color NORMALCOLOR=new Color(192,255,192);
    
    /**
     * Color of the options button when the GPS is connected but not locked on
     * satellites RGB=(240,120,0).
     */
    public static final Color LOSTCOLOR=new Color(240,120,0);
    
    /**
     * Color of the options button when the GPS is not connected. RGB=(160,0,0).
     */
    public static final Color BADCOLOR=new Color(160,0,0);
    
    /** Fixed default name of the option file. */
    public static final String OPTIONSFILENAME="courselog.ini";
    
    /** Application serial GPS connection */
    protected SerialGPSConnection mySGC=null;
    
    /** Serial port options to connect with GPS */
    protected SerialPortOptions mySPO=null;
    
    /** Tag indicating the GPS port options in the options file. */
    public static final String TAGSPO="GPS=";
    
    /** Tag indicating the units in the options file. */
    public static final String TAGUNITS="UnitsIndex=";
    
    /** Log file to log moves. */
    protected String myLogFileName=null;
    
    /** Initial status of the application */
    protected int myInitialStatus;
    
    /** Default name of the log file */
    public static final String DEFAULTLOGFILE="tmp.txt";
    
    /** Tag indicating the log file name in the options file. */
    public static final String TAGLOG="Log=";
    
    /** Minium distance between logged moves. */
    protected double myMinDistance=50.0d;
    
    /** Tag indicating the min distance value in the options file. */
    public static final String TAGMINDIST="MinDistance=";
    
    /** Application current maps directory */
    protected String myMapDir=null;
    
    /** Default name of the map directory */
    public static final String DEFAULTMAPDIR="maps";
    
    /** Tag indicating the map directory in the options file. */
    public static final String TAGMAPDIR="Maps=";
    
    /** Application last used waypoints file */
    protected String myLastWaypoints=null;
    
    /** Tag indicating the last waypoints file. */
    public static final String TAGLASTWP="LastWaypoints=";
    
    /** Value of last next waypoint read from options file. */
    protected int myLastNext=-1;
    
    /** Tag indicating the last selected waypoint. */
    public static final String TAGLASTSELECTED="LastSelected=";
    
    /** Value of last selected waypoint read from options file. */
    protected int myLastSelected=-1;
    
    /** Tag indicating the last next point to aim at. */
    public static final String TAGLASTNEXT="LastNext=";
    
    /** Tag indicating last active panel in the options file. */
    public static final String TAGLAST="Last=";
    
    /** Tag indicating the last status of the application in the options file. */
    public static final String TAGSTATUS="TracerStatus=";
    
    /** Tag indicating last status of the navigation form. */
    public static final String TAGNAVLAST="NavigationLastStatus=";
    
    /** Application Track tracer */
    protected PositionTracer myTr;
    
    /** Application next active panel */
    protected int myNextActivePanel=0;
    
    /** Application last active panel */
    protected int myLastActivePanel=0;
    
    /** Calibration panel */
    protected CalibrateForm myCalibForm=null;
    
    /** Index indicating Calibration panel is active */
    public static final int ACTIVECALIBRATION=0;
    
    /** Options panel */
    protected OptionsForm myOptionsForm=null;
    
    /** Index indicating Options panel is active */
    public static final int ACTIVEOPTIONS=1;
    
    /** Convert panel */
    protected ConvertForm myConvertForm=null;
    
    /** Index indicating Convert panel is active */
    public static final int ACTIVECONVERT=2;
    
    /** Distance panel */
    protected DistanceForm myDistanceForm=null;
    
    /** Index indicating Distance panel is active */
    public static final int ACTIVEDISTANCE=3;
    
    /** Navigation panel */
    protected NavigateForm myNavigateForm=null;
    
    /** Index indicating Distance panel is active */
    public static final int ACTIVENAVIGATE=4;
    
    /** Creates a new instance of MainApplication */
    public MainApplication() {
    }
    
    public static void main(String args[]) {
        Vm.startEwe(args);
        try {
            
            MainApplication crseLog=new MainApplication();
            crseLog.readOptionsFile();
            TiledMapBrowser tm=crseLog.getMyNavigateForm().tmb;
            if(crseLog.myLastWaypoints!=null) {
                File fW=new File(crseLog.myLastWaypoints);
                if(fW.canRead()) {
                    tm.myTrWaypoints.loadFromFile(fW);
                }
            }
            tm.setNextWaypoint(crseLog.myLastNext); 
            tm.setSelectedWaypoint(crseLog.myLastSelected); 
            
            crseLog.myTr=new PositionTracer(crseLog.getMySGC(),crseLog.myInitialStatus,
                    crseLog.myLogFileName);
            crseLog.myTr.minDistance=crseLog.myMinDistance;
            tm.setCurrentTrack(crseLog.myTr.myTrack);
            
            crseLog.myTr.addDisplayer(crseLog.getMyDistanceForm());
            crseLog.myTr.addDisplayer(tm);
            
            crseLog.myNextActivePanel=crseLog.myLastActivePanel;
            while(crseLog.myNextActivePanel>=0) {
                crseLog.myLastActivePanel=crseLog.myNextActivePanel;
                switch(crseLog.myNextActivePanel) {
                    case ACTIVECALIBRATION:
                        crseLog.getMyCalibForm().execute();
                        break;
                    case ACTIVECONVERT:
                        crseLog.getMyConvertForm().execute();
                        break;
                    case ACTIVEDISTANCE:
                        DistanceForm dF=crseLog.getMyDistanceForm();
                        dF.updateTracerStatus(crseLog.myTr);
                        dF.updateTrackData(crseLog.myTr);
                        dF.execute();
                        break;
                    case ACTIVEOPTIONS:
                        crseLog.getMyOptionsForm().execute();
                        crseLog.updateOptions();
                        break;
                    case ACTIVENAVIGATE:
                        crseLog.getMyNavigateForm().execute();
                        break;
                    default:
                }
                if(crseLog.myLastActivePanel==crseLog.myNextActivePanel) {
                    // No change within the form : exit was pressed
                    crseLog.myNextActivePanel=-1;
                }
            }
            crseLog.writeOptionsFile();
        } catch (NullPointerException ex) {
            CourseLogAppForm.dbgL.addExceptionToLog(ex);
        } catch (IndexOutOfBoundsException ex) {
            CourseLogAppForm.dbgL.addExceptionToLog(ex);
        } catch (ClassCastException ex) {
            CourseLogAppForm.dbgL.addExceptionToLog(ex);
        } catch (RuntimeException ex) {
            CourseLogAppForm.dbgL.addExceptionToLog(ex);
        }
        Vm.exit(0);
    }
    
    // ACCESSORS
    
    public String getMyMapDir() {
        return myMapDir;
    }
    
    public SerialGPSConnection getMySGC() {
        if(mySGC==null) {
            mySGC=SerialGPSConnection.getAnActiveSerialGPSConnection(getMySPO(),null);
        }
        return mySGC;
    }
    
    public SerialPortOptions getMySPO() {
        if(mySPO==null) {
            mySPO=SerialGPSConnection.getDefaultPortOptions();
        }
        return mySPO;
    }
    
    public CalibrateForm getMyCalibForm() {
        if(myCalibForm==null) myCalibForm=new CalibrateForm(this);
        return myCalibForm;
    }
    
    public ConvertForm getMyConvertForm() {
        if(myConvertForm==null) myConvertForm=new ConvertForm(this);
        return myConvertForm;
    }
    
    public DistanceForm getMyDistanceForm() {
        if(myDistanceForm==null) myDistanceForm=new DistanceForm(this);
        return myDistanceForm;
    }
    
    public OptionsForm getMyOptionsForm() {
        if(myOptionsForm==null) myOptionsForm=new OptionsForm(this);
        return myOptionsForm;
    }
    
    public NavigateForm getMyNavigateForm() {
        if(myNavigateForm==null) myNavigateForm=new NavigateForm(this);
        return myNavigateForm;
    }
    
    // CLASS STATIC METHODS
    
    /** Build the default map directory from the application path. */
    public static String getDefaultMapDirPath() {
        return File.makePath(File.getProgramDirectory(),
                DEFAULTMAPDIR);
    }
    
    /** Build the default log file name from the application path. */
    public static String getDefaulLogFilePath() {
        return File.makePath(File.getProgramDirectory(),
                DEFAULTLOGFILE);
    }
    
    /** Default minimum distance between log points == 50.0d. */
    public static double getDefaulMinDistance() {
        return 50.0d;
    }
    
    /** Default units set km/h and m. */
    public static int getDefaulUnits() {
        return Converters.FORMAT_KM_M;
    }
    
    // OBJECT CLASS METHODS
    
    public void initToDefaultOptions() {
        myMapDir=MainApplication.getDefaultMapDirPath();
        myLogFileName=MainApplication.getDefaulLogFilePath();
        myMinDistance=MainApplication.getDefaulMinDistance();
        myNextActivePanel=ACTIVEOPTIONS;
        mySPO=getMySPO();
        myLastActivePanel=ACTIVEOPTIONS;
        myInitialStatus=PositionTracer.STATUS_OFF;
        Converters.classCurrentFormat=Converters.FORMAT_KM_M;
    }
    
    /**
     * Write "OPTIONSFILENAME" file in the program directory with the current
     * options settings. The options should be initialised, at least by
     * initToDefaultOptions().
     * @return true if the file could be correctly written, false otherwise.
     */
    public boolean writeOptionsFile() {
        File fOpt=new File(File.getProgramDirectory(),OPTIONSFILENAME);
        if((fOpt.exists())&&(!fOpt.canWrite())) return false;
        try {
            FileWriter fW=new FileWriter(fOpt);
            fW.println(TAGMAPDIR+myMapDir);
            fW.println(TAGLOG+myLogFileName);
            if(myLastWaypoints!=null) {
                fW.println(TAGLASTWP+myLastWaypoints);
            }
            if(myNavigateForm!=null) {
                fW.println(TAGNAVLAST+myNavigateForm.tmb.myState);
                int iSel=myNavigateForm.tmb.getNextWaypoint();
                if(iSel>=0){
                    fW.println(TAGLASTNEXT+iSel);
                }
                iSel=myNavigateForm.tmb.getSelectedWaypoint();
                if(iSel>=0){
                    fW.println(TAGLASTSELECTED+iSel);
                }
            }
            fW.println(TAGMINDIST+Convert.formatDouble(myMinDistance));
            fW.println(TAGUNITS+Convert.formatInt(Converters.classCurrentFormat));
            fW.println(TAGSPO+SerialGPSConnection.getPortOptionsAsString(getMySPO()));
            fW.println(TAGLAST+Convert.formatInt(myLastActivePanel));
            fW.print(TAGSTATUS);
            switch(myTr.myStatus) {
                case PositionTracer.STATUS_HOLD:
                    fW.println("HOLD"); break;
                case PositionTracer.STATUS_PAUSED:
                    fW.println("PAUSED"); break;
                case PositionTracer.STATUS_RECORDING:
                    // Recording means hold mode when restart
                    fW.println("RECORDING"); break;
                default:
                    fW.println("OFF");
            }
            fW.close();
        } catch (SecurityException ex) {
            CourseLogAppForm.dbgL.addExceptionToLog(ex);
            return false;
        } catch (IOException ex) {
            CourseLogAppForm.dbgL.addExceptionToLog(ex);
            return false;
        }
        return true;
    }
    
    /**
     * Read "OPTIONSFILENAME" file in the program directory to generate the options
     * as they were recorded at last exit.
     * @return true if the file could be correctly read, false otherwise.
     */
    public boolean readOptionsFile() {
        // First init to default options to see if the file will complete them
        initToDefaultOptions();
        // Open options file
        File fOpt=new File(File.getProgramDirectory(),OPTIONSFILENAME);
        if((!fOpt.exists())||(!fOpt.canRead())) return false;
        try {
            FileReader fR=new FileReader(fOpt);
            // Read until blocking (means end-of-file on every system?)
            String ln=fR.readLine();
            while(ln!=null) {
                if(ln.startsWith(TAGSPO)) {
                    mySPO=SerialGPSConnection.getPortOptionsFromString(
                            ln.substring(TAGSPO.length()));
                }else if(ln.startsWith(TAGMAPDIR)) {
                    myMapDir=ln.substring(TAGMAPDIR.length()).trim();
                }else if(ln.startsWith(TAGLOG)) {
                    myLogFileName=ln.substring(TAGLOG.length()).trim();
                }else if(ln.startsWith(TAGLASTWP)) {
                    myLastWaypoints=ln.substring(TAGLASTWP.length()).trim();
                    if(myLastWaypoints.length()<1) {
                        myLastWaypoints=null;
                    }
                }else if(ln.startsWith(TAGLASTNEXT)) {
                    myLastNext=Convert.parseInt(ln.substring(
                            TAGLASTNEXT.length()).trim());
                }else if(ln.startsWith(TAGLASTSELECTED)) {
                    myLastSelected=Convert.parseInt(ln.substring(
                            TAGLASTSELECTED.length()).trim());
                }else if(ln.startsWith(TAGMINDIST)) {
                    myMinDistance=Convert.parseDouble(ln.substring(
                            TAGMINDIST.length()).trim());
                }else if(ln.startsWith(TAGUNITS)) {
                    Converters.classCurrentFormat=Convert.parseInt(ln.substring(
                            TAGUNITS.length()));
                }else if(ln.startsWith(TAGLAST)) {
                    myLastActivePanel=Convert.parseInt(ln.substring(
                            TAGLAST.length()));
                }else if(ln.startsWith(TAGNAVLAST)) {
                    NavigateForm nav=getMyNavigateForm();
                    nav.setMyTMState(Convert.parseInt(ln.substring(
                            TAGNAVLAST.length())));
                }else if(ln.startsWith(TAGSTATUS)) {
                    String sVal=ln.substring(TAGSTATUS.length());
                    sVal=sVal.trim();
                    if(sVal.equalsIgnoreCase("HOLD")) {
                        myInitialStatus=PositionTracer.STATUS_HOLD;
                    }else if(sVal.equalsIgnoreCase("OFF")) {
                        myInitialStatus=PositionTracer.STATUS_OFF;
                    }else if(sVal.equalsIgnoreCase("PAUSED")) {
                        myInitialStatus=PositionTracer.STATUS_PAUSED;
                    }else if(sVal.equalsIgnoreCase("RECORDING")) {
                        myInitialStatus=PositionTracer.STATUS_RECORDING;
                    }
                }
                ln=fR.readLine();
            }
            fR.close();
        } catch (NumberFormatException ex) {
            CourseLogAppForm.dbgL.addExceptionToLog(ex);
            return false;
        } catch (FileNotFoundException ex) {
            CourseLogAppForm.dbgL.addExceptionToLog(ex);
            return false;
        } catch (IOException ex) {
            CourseLogAppForm.dbgL.addExceptionToLog(ex);
            return false;
        }
        return true;
    }
    
    /**
     * Initialise the tracer with current options.
     */
    public void updateOptions() {
        OptionsForm optF=getMyOptionsForm();
        myMinDistance=optF.minDistance;
        Converters.classCurrentFormat=optF.mySelectedUnits;
        myMapDir=optF.myMapDir;
// Put that if necessary but the user can command it from the menus
//        if(myNavigateForm!=null) {
//            myNavigateForm.reLoad();
//        }
        if(mySPO!=optF.dialogSPO) {
            int oldSt=myTr.myStatus;
            myTr.setStatus(PositionTracer.STATUS_OFF);
            mySPO=optF.dialogSPO;
            myLogFileName=optF.myLogFileName;
            mySGC=null; // Release SGC to create a new one
            //CourseLogAppForm.dbgL.addToLog("MainApplication.updateOptions : new PositionTracer\n");
            myTr=new PositionTracer(getMySGC(),oldSt,myLogFileName);
            //CourseLogAppForm.dbgL.addToLog("MainApplication.updateOptions : getMyDistanceForm\n");
            getMyDistanceForm().setCurrentTrack(myTr.myTrack);
            //CourseLogAppForm.dbgL.addToLog("MainApplication.updateOptions : getMyNavigateForm\n");
            getMyNavigateForm().tmb.setCurrentTrack(myTr.myTrack);
            //CourseLogAppForm.dbgL.addToLog("MainApplication.updateOptions : addDisplayer(dist)\n");
            myTr.addDisplayer(myDistanceForm);
            //CourseLogAppForm.dbgL.addToLog("MainApplication.updateOptions : addDisplayer(nav)\n");
            myTr.addDisplayer(myNavigateForm.tmb);
        }
        
        //CourseLogAppForm.dbgL.addToLog("MainApplication.updateOptions : updated\n");
        myTr.minDistance=myMinDistance;
        
        if(!myLogFileName.equals(optF.myLogFileName)) {
            myLogFileName=optF.myLogFileName;
            int prevSt=myTr.myStatus;
            myTr.setStatus(PositionTracer.STATUS_OFF);
            myTr.setLogFileName(myLogFileName);
            myTr.setStatus(prevSt);
        }
    }
}
